package com.boe.csb.web.controller.base;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import com.boe.csb.core.common.exception.CSBErrorCode;
import com.boe.csb.core.common.exception.CSBException;
import com.boe.csb.core.common.status.CertificationStatus;
import com.boe.csb.core.common.status.CertificationType;
import com.boe.csb.core.common.status.UserPicType;
import com.boe.csb.core.common.utils.FtpUtil;
import com.boe.csb.core.common.utils.MD5Utils;
import com.boe.csb.core.common.utils.PropertyUtils;
import com.boe.csb.core.common.utils.VerifyCodeUtils;
import com.boe.csb.core.dao.BoeApiInfoMapper;
import com.boe.csb.core.dao.BoeUserMapper;
import com.boe.csb.core.entity.ResultInfo;
import com.boe.csb.core.entity.SimplePageInfo;
import com.boe.csb.core.entity.UserInfo;
import com.boe.csb.core.entity.dto.BoeUserDTO;
import com.boe.csb.core.entity.po.BoeApiInfo;
import com.boe.csb.core.entity.po.BoeUser;
import com.boe.csb.core.entity.po.BoeUserFtp;
import com.boe.csb.core.service.user.BoeUserFtpService;
import com.boe.csb.core.service.user.BoeUserService;
import com.boe.csb.web.controller.BaseController;
import com.boe.csb.web.controller.base.vo.boeuser.EnterpriseUser;
import com.boe.csb.web.controller.base.vo.boeuser.IndividualUser;
import com.boe.csb.web.controller.base.vo.boeuser.InsideUser;
import com.boe.csb.web.converter.ResultInfoToModule;
import com.boe.csb.web.model.ResultModel;

/**
 * Created by tianxiang.luo on 16/11/25.
 */
@RestController
@RequestMapping("api/user/")
@Validated
public class BoeUserController extends BaseController {

    private static final Logger logger = LoggerFactory.getLogger(BoeUserController.class);
    private static String CHARSET = "utf-8";
    /** 设置在激活token中的分隔符*/
    private static final String TOKEN_SP = (char) 29 + "";//隐藏字符

    @Autowired
    private BoeUserService boeUserService;
    @Autowired
    private BoeUserMapper boeUserMapper;
    @Autowired
    private BoeUserFtpService boeUserFtpService;
    @Autowired
    private BoeApiInfoMapper boeApiInfoMapper;
    
    private static String insideAuthentication;

    static {
        insideAuthentication = PropertyUtils.getProperty("boe.inside.authentication");
    }

    /**
     * 企业认证
     */
    @RequestMapping(value = "/certification/EnterpriseUser", method = RequestMethod.POST)
    public ResultModel<Void> userCertificationEnter(HttpSession session, @ModelAttribute @Validated EnterpriseUser enterprise, BindingResult result) throws IOException {
        ResultModel resultModel = new ResultModel();
        if (result.hasErrors()) {
            return returnFieldErrorResult(resultModel, result);
        }
        String userId = fetchUserId(session);
        BoeUser boeUser = boeUserService.findBoeUser(userId);
        //已经通过审核了就不能修改信息
        if (CertificationStatus.ALLOW.getStatus().equals(boeUser.getAuditStatus())) {
            throw new CSBException(CSBErrorCode.USER_CERTIFICATION, "");
        }
        if(boeUser.getCertificationType() != null && !CertificationType.ENTERPRISE.getType().equals(boeUser.getCertificationType())){
            throw new CSBException(CSBErrorCode.CERTIFICATION_TYPE_CHANGE, "");
        }
        BeanUtils.copyProperties(enterprise, boeUser);
        boeUser.setCertificationType(CertificationType.ENTERPRISE.getType());
        Map<String, CommonsMultipartFile> picMap = new HashMap<>();
        picMap.put(CertificationType.ENTERPRISE.getType(), enterprise.getBusinessLicenseFile());
        boeUser.setAuditStatus(CertificationStatus.APPLYING.getStatus());
        boeUserService.userCertification(picMap, boeUser, CertificationType.ENTERPRISE.getType());
        return ResultModel.success(null);
    }

    /**
     * 个人用户认证
     */
    @RequestMapping(value = "/certification/IndividualUser", method = RequestMethod.POST)
    public ResultModel<Void> userCertificationIndiv(HttpSession session, @ModelAttribute @Validated IndividualUser individual, BindingResult result) throws IOException {
        ResultModel resultModel = new ResultModel();
        if (result.hasErrors()) {
            return returnFieldErrorResult(resultModel, result);
        }
        String userId = fetchUserId(session);
        BoeUser boeUser = boeUserService.findBoeUser(userId);
        if (CertificationStatus.ALLOW.getStatus().equals(boeUser.getAuditStatus())) {
            throw new CSBException(CSBErrorCode.USER_CERTIFICATION, "");
        }
        if(boeUser.getCertificationType() != null && !CertificationType.INDIVIDUAL.getType().equals(boeUser.getCertificationType())){
            throw new CSBException(CSBErrorCode.CERTIFICATION_TYPE_CHANGE, "");
        }
        BeanUtils.copyProperties(individual, boeUser);
        boeUser.setCertificationType(CertificationType.INDIVIDUAL.getType());
        Map<String, CommonsMultipartFile> picMap = new HashMap<>();
        picMap.put(CertificationType.INDIVIDUAL.getType(), individual.getIdCardFile());
        boeUser.setAuditStatus(CertificationStatus.APPLYING.getStatus());
        boeUserService.userCertification(picMap, boeUser, CertificationType.INDIVIDUAL.getType());
        return ResultModel.success(null);
    }

    /**
     * 内部用户认证
     */
    @RequestMapping(value = "/certification/InsideUser", method = RequestMethod.POST)
    public ResultModel<Void> userCertificationIns(HttpSession session, @ModelAttribute @Validated InsideUser inside, BindingResult result) throws IOException {
        ResultModel resultModel = new ResultModel();
        if (result.hasErrors()) {
            return returnFieldErrorResult(resultModel, result);
        }
        String userId = fetchUserId(session);
        BoeUser boeUser = boeUserService.findBoeUser(userId);
        if (CertificationStatus.ALLOW.getStatus().equals(boeUser.getAuditStatus())) {
            throw new CSBException(CSBErrorCode.USER_CERTIFICATION, "");
        }
        if(boeUser.getCertificationType() != null && !CertificationType.INSIDE.getType().equals(boeUser.getCertificationType())){
            throw new CSBException(CSBErrorCode.CERTIFICATION_TYPE_CHANGE, "");
        }
        BeanUtils.copyProperties(inside, boeUser);
        boeUser.setCertificationType(CertificationType.INSIDE.getType());
        boeUser.setAuditStatus(CertificationStatus.APPLYING.getStatus());
        boeUserService.userCertification(null, boeUser, CertificationType.INSIDE.getType());
        return ResultModel.success(null);
    }


    /**
     * 查看用户基础信息
     *
     * @param userId
     * @return
     */
    @RequestMapping(value = "/secret/info", method = RequestMethod.GET)
    public ResultModel<BoeUserDTO> secretInfo(HttpSession session, @RequestParam(required = false) Long userId,HttpServletResponse response) {
    	BoeUser boeUser;
        UserInfo tmpUser = fetchUserInfo(session);
        //是不是查看自己的信息    首先查看是不是管理员
        if (tmpUser.getIsAdmin()) {
            if(userId==null){
                throw new CSBException(CSBErrorCode.PARAM_ERR,"");
            }
            boeUser = boeUserService.findBoeUser(String.valueOf(userId));
            boeUser.setAk(null);
            boeUser.setSk(null);
        } else {
            boeUser = boeUserService.findBoeUser(String.valueOf(tmpUser.getUserId()));
        }
        BoeUserDTO dto = new BoeUserDTO();
        BeanUtils.copyProperties(boeUser, dto);
        boeUser.setPassword(null);
        if (boeUser.getCertificationType() != null) {
            switch (boeUser.getCertificationType()) {
                case "individual_certification":
                    dto.setIdCardPic("/api/user/getImg?userId=" + boeUser.getId() + "&type=" + UserPicType.idCard.name());
                    break;
                case "enterprise_certification":
                    dto.setBusinessLicensePic("/api/user/getImg?userId=" + boeUser.getId() + "&type=" + UserPicType.businessLicense.name());
                    break;
            }
        }
        return ResultModel.success(dto);
    }

    /**
     * 用户的详情
     *
     * @param session
     * @param userId
     * @return
     */
    @RequestMapping(value = "/info", method = RequestMethod.GET)
    @ResponseBody
    public ResultModel<BoeUserDTO> info(HttpSession session, @RequestParam(required = false) Long userId,@RequestParam(required = false) Long serviceId) {
    	if (userId == null) {
        	String uid = fetchUserId(session);
        	if(StringUtils.isBlank(uid)){
        		BoeApiInfo boeApiInfo = boeApiInfoMapper.loadApiInfo(Long.valueOf(serviceId));
        		userId = boeApiInfo.getUserId();
        		userLogin(userId, session);
        	}else{
        		userId = Long.valueOf(uid);
        	}
        }
        
        BoeUserDTO dto = new BoeUserDTO();
        BoeUser boeUser = boeUserService.findBoeUser(String.valueOf(userId));
        BeanUtils.copyProperties(boeUser, dto);
        if (boeUser.getCertificationType() != null) {
            switch (boeUser.getCertificationType()) {
                case "individual_certification":
                    dto.setIdCardPic("/api/user/getImg?userId=" + boeUser.getId() + "&type=" + UserPicType.idCard.name());
                    break;
                case "enterprise_certification":
                    dto.setBusinessLicensePic("/api/user/getImg?userId=" + boeUser.getId() + "&type=" + UserPicType.businessLicense.name());
                    break;
            }
        }
        return ResultModel.success(dto);
    }


    @RequestMapping(value = "checklogin", method = RequestMethod.GET)
    public ResultModel<UserInfo> checkIsLogin(HttpSession httpSession) {
        Object isLogin = httpSession.getAttribute(LOGIN_FLAG);
        if (isLogin == null || !(Boolean) isLogin) {
            return ResultModel.fail(CSBErrorCode.NOT_LOGIN, null);
        }

        UserInfo userInfo = (UserInfo) httpSession.getAttribute(USER_KEY);
        if (userInfo == null) {//数据异常设置为未登录
            return ResultModel.fail(CSBErrorCode.NOT_LOGIN, null);
        }

        return ResultModel.success(userInfo);
    }

    @RequestMapping(value = "login", method = RequestMethod.POST)
    public ResultModel<UserInfo> login(@NotEmpty(message = "用户名不能为空") @RequestParam String userName,
                                       @NotEmpty(message = "密码不能为空") @RequestParam String pwd,
                                       String verifyCode, HttpSession httpSession, HttpServletResponse response) {
        String cacheVerifyCode = VerifyCodeUtils.getCacheVerifyCode(httpSession, VERIFY_CODE_KEY, true);
        if (cacheVerifyCode == null || !cacheVerifyCode.equalsIgnoreCase(verifyCode.trim())) {
            logger.error("session:{} login fail cause of verify code not correct. post:{}, cache:{}.", new Object[]{httpSession.getId(), verifyCode, cacheVerifyCode});
            return ResultModel.fail(CSBErrorCode.VERIFY_CODE_ERR, null);
        }

        if (boeUserService.checkISAdmin(userName)) {//管理员不允许登录普通页面
            return ResultModel.fail(CSBErrorCode.USER_IS_ADMIN, null);
        }

        return userLogin(userName, pwd, httpSession, response);
    }


    /**
     * 管理员登录
     *
     * @param userName
     * @param pwd
     * @param httpSession
     * @param response
     * @return
     */
    @RequestMapping(value = "adminlogin", method = RequestMethod.POST)
    public ResultModel<UserInfo> adminLogin(@RequestParam @NotEmpty(message = "用户名不能为空") String userName,
                                            @RequestParam @NotEmpty(message = "用户名不能为空") String pwd,
                                            HttpSession httpSession, HttpServletResponse response) {
        if (!boeUserService.checkISAdmin(userName)) {
            return ResultModel.fail(CSBErrorCode.USER_NOT_ADMIN, null);
        }

        return userLogin(userName, pwd, httpSession, response);
    }

    private ResultModel<UserInfo> userLogin(String userName, String pwd, HttpSession httpSession, HttpServletResponse response) {
        ResultInfo<UserInfo> resultInfo = boeUserService.userLogin(userName, pwd);
        if (resultInfo.getErrorCode() == CSBErrorCode.SUCCESS) {//存session
            httpSession.setAttribute(LOGIN_FLAG, true);
            httpSession.setAttribute(USER_KEY, resultInfo.getResult());

            //写cookie
            String uidStr = resultInfo.getResult().getUserId() + "";
            Boolean isAdmin = resultInfo.getResult().getIsAdmin();
            String isAdminStr = (isAdmin != null && isAdmin) ? "true" : "false";
            addCookie(response, "uid", uidStr);
            addCookie(response, "username", userName);
            addCookie(response, "isadmin", isAdminStr);

            BoeUser boeUser = new BoeUser();
            boeUser.setId(resultInfo.getResult().getUserId());
            boeUser.setGmtLogin(new Date());
            boeUserMapper.updateByPrimaryKeySelective(boeUser);
        }

        return ResultInfoToModule.getModule(resultInfo);
    }
    
    private ResultModel<UserInfo> userLogin(Long userId, HttpSession httpSession) {
        ResultInfo<UserInfo> resultInfo = boeUserService.userLogin(userId);
        if (resultInfo.getErrorCode() == CSBErrorCode.SUCCESS) {//存session
            httpSession.setAttribute(LOGIN_FLAG, true);
            httpSession.setAttribute(USER_KEY, resultInfo.getResult());
        }
        return ResultInfoToModule.getModule(resultInfo);
    }

    /**
     * 
     * @param userName
     * @param pwd
     * @param revNum
     * @param mobileCode
     * @param httpSession
     * @return
     */
    @RequestMapping(value = "regist", method = RequestMethod.POST)
    public ResultModel<UserInfo> regist(@RequestParam String userName, @RequestParam String pwd, @RequestParam String revNum, @RequestParam String mobileCode, HttpSession httpSession) {

        //校验手机验证码
        CSBErrorCode ercode = boeUserService.verifyMobileCode(userName, revNum, mobileCode);
        if(!ercode.equals(CSBErrorCode.SUCCESS)){
        	return ResultModel.fail(ercode, null);
        }

        ResultInfo<UserInfo> resultInfo = boeUserService.userRegist(userName, revNum, pwd,httpSession);
        return ResultInfoToModule.getModule(resultInfo);
    }

    /**
     * 更新密码
     */
    @RequestMapping(value = "update/pwd")
    public ResultModel<Void> updatePwd(HttpSession session, HttpServletRequest req,HttpServletResponse res,@RequestParam String oldPwd, @RequestParam String newPwd){
        Long userId = fetchUserInfo(session).getUserId();
        BoeUser boeUser = boeUserService.findBoeUser(String.valueOf(userId));
        if(!boeUser.getPassword().equals(MD5Utils.getMD5String(oldPwd))){
            throw new CSBException(CSBErrorCode.USER_OLD_PWD_ERR,"");
        }
        BoeUser update = new BoeUser();
        update.setId(userId);
        update.setPassword(MD5Utils.getMD5String(newPwd));
        boeUserMapper.updateByPrimaryKeySelective(update);

        session.setAttribute(LOGIN_FLAG, false);//移除session
        session.setAttribute(USER_KEY, false);
        clearCookie(req, res);
        return ResultModel.success(null);
    }


    /**
     * 忘记密码, 系统重置一个随机密码, 然后发送到管理员邮箱
     */
    @RequestMapping(value = "forget/password", method = RequestMethod.POST)
    public ResultModel<Object> forgetPassword(@RequestParam String email) {
        boeUserService.forgetPassword(email);
        return ResultModel.success(null);
    }

    @RequestMapping(value = "logout", method = RequestMethod.POST)
    public ResultModel<Void> logout(HttpSession httpSession, HttpServletRequest req, HttpServletResponse res) {
        httpSession.setAttribute(LOGIN_FLAG, false);//移除session
        httpSession.setAttribute(USER_KEY, false);
        clearCookie(req, res);
        return ResultModel.success(null);
    }

    @RequestMapping(value = "getVerifyCode", method = RequestMethod.POST)
    public ResultModel<String> getVerifyCode(HttpSession httpSession) {
        String verifyCode = VerifyCodeUtils.generateVerifyCode();
        logger.debug("http session{}: get verifycode:{}.", httpSession.getId(), verifyCode);
        httpSession.setAttribute(VERIFY_CODE_KEY, verifyCode);
        return ResultModel.success(verifyCode);
    }

    /**
     * 内部用户激活自动登录加载session
     * @param token
     * @return
     */
    @RequestMapping(value = "activate", method = RequestMethod.GET)
    public ResultModel<Object> userActivate(@RequestParam String token,HttpSession httpSession,HttpServletResponse response) {
        if (StringUtils.isEmpty(token)) {
            return ResultModel.fail(CSBErrorCode.UNKNOWN_ERROR, null);
        }
        
        String decodeToken = null;
        try {
            decodeToken = URLDecoder.decode(token, CHARSET);
        } catch (UnsupportedEncodingException e) {
            logger.error("error:", e);
            return ResultModel.fail(CSBErrorCode.UNKNOWN_ERROR,null);
        }

        String[] infos = decodeToken.split(TOKEN_SP);
        if (infos.length != 3) {
            logger.error("invalid token info:{}.", token);
            return ResultModel.fail(CSBErrorCode.ACTIVE_INFO_ERR,null);
        }

        String userName = infos[1];
        CSBErrorCode errCode = boeUserService.activeUser(decodeToken,infos[2]);
        if(infos[1].endsWith(insideAuthentication)){
        	ResultInfo<UserInfo> resultInfo =  boeUserService.userLogin(userName);  
        	if (resultInfo.getErrorCode() == CSBErrorCode.SUCCESS) {//存session
                httpSession.setAttribute(LOGIN_FLAG, true);
                httpSession.setAttribute(USER_KEY, resultInfo.getResult());

                //写cookie
                String uidStr = resultInfo.getResult().getUserId() + "";
                Boolean isAdmin = resultInfo.getResult().getIsAdmin();
                String isAdminStr = (isAdmin != null && isAdmin) ? "true" : "false";
                addCookie(response, "uid", uidStr);
                addCookie(response, "username", userName);
                addCookie(response, "isadmin", isAdminStr);

                BoeUser boeUser = new BoeUser();
                boeUser.setId(resultInfo.getResult().getUserId());
                boeUser.setGmtLogin(new Date());
                boeUserMapper.updateByPrimaryKeySelective(boeUser);
            }
        }
        return errCode == CSBErrorCode.SUCCESS ? ResultModel.success(null) : ResultModel.fail(errCode, null);
    }

    @RequestMapping(value = "sendRegMobileCode", method = RequestMethod.GET)
    public ResultModel<Object> sendRegMobileCode(String userName, String revNum) {
        CSBErrorCode errorCode = boeUserService.sendMobileRegCode(userName, revNum);
        return errorCode == CSBErrorCode.SUCCESS ? ResultModel.success(null) : ResultModel.fail(errorCode, null);
    }

    /**
     * 测试获取图片
     *
     * @param response
     * @throws IOException
     */
    @RequestMapping(value = "getImg", method = RequestMethod.GET)
    public void getImg(HttpServletResponse response, @RequestParam Long userId, @RequestParam String type,@RequestParam(required = false) Long csbApiId) throws IOException {
        BoeUserFtp ftp = boeUserFtpService.findPathByUserIdAndType(String.valueOf(userId), type,csbApiId);
        if (ftp == null) {
            logger.error("get pic by userId:{}, type:{}!!", userId, type);
            return;
        }
        String fileName = ftp.getFilename();
        String filePath = ftp.getFilepath();
        byte[] bytes = FtpUtil.download(filePath, fileName);
        if (bytes == null) {
            logger.info("pic is not found of path:{}, filelname:{}!!", filePath, fileName);
            return;
        }

        OutputStream os = response.getOutputStream();
        InputStream in = new ByteArrayInputStream(bytes);
        int len = 0;
        byte[] buf = new byte[1024];
        while ((len = in.read(buf, 0, 1024)) != -1) {
            os.write(buf, 0, len);
        }
        os.flush();
    }

    /**
     * 获取图片
     *
     * @param request
     * @param response
     * @param type
     * @throws IOException
     */
    @RequestMapping(value = "get/img", method = RequestMethod.GET)
    public void getImage(HttpServletRequest request, HttpServletResponse response, @RequestParam String type,@RequestParam(required = false) Long csbApiId) throws IOException {
        // session中取数据
        UserInfo userInfo = (UserInfo) request.getSession().getAttribute(USER_KEY);
        String userId = String.valueOf(userInfo.getUserId());

        BoeUserFtp boeUserFtp = boeUserFtpService.findPathByUserIdAndType(userId, type,csbApiId);
        if (boeUserFtp == null) {
            logger.info("can not find BoeUserFtp by userid:{}, type:{}.", userId, type);
            return;
        }

        String filePath = boeUserFtp.getFilepath();
        String fileName = boeUserFtp.getFilename();

        byte[] bytes = FtpUtil.download(filePath, fileName);
        OutputStream os = response.getOutputStream();
        InputStream in = new ByteArrayInputStream(bytes);
        int len = 0;
        byte[] buf = new byte[1024];
        while ((len = in.read(buf, 0, 1024)) != -1) {
            os.write(buf, 0, len);
        }
        os.flush();
    }


    /**
     * 查询用户
     * @return
     */
    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public ResultModel<SimplePageInfo> queryUser(@RequestParam String status, @RequestParam Integer pageNum, @RequestParam Integer limit) {
        return ResultModel.success(new SimplePageInfo(boeUserService.queryUser(status, pageNum, limit),boeUserService.countUser(status)));
    }

    /**
     * 判断用户是否未登入
     *
     * @param session
     * @return
     */
    @RequestMapping(value = "/validate/login", method = RequestMethod.POST)
    public ResultModel<String> validateLogin(HttpSession session) {
        UserInfo userInfo = (UserInfo) session.getAttribute(USER_KEY);
        if (userInfo != null) {
            String email = boeUserService.findBoeUser(String.valueOf(userInfo.getUserId())).getEmail();
            StringBuffer sb = new StringBuffer();
            int index = email.indexOf("@");
            String tailEmail = email.substring(index, email.length());
            sb.append(email.charAt(0)).append(email.charAt(1)).append("*****").append(tailEmail);
            return ResultModel.success(sb.toString());
        }
        return ResultModel.success(null);
    }

    public void clearCookie(HttpServletRequest req, HttpServletResponse res) {
        Cookie[] cookies = req.getCookies();
        for (int i = 0, len = cookies.length; i < len; i++) {
            Cookie cookie = new Cookie(cookies[i].getName(), null);
            cookie.setMaxAge(0);
            cookie.setPath("/");//如果经过nginx代理需要在nginx上设置cookie路径
            res.addCookie(cookie);
        }
    }

    public void addCookie(HttpServletResponse res, String key, String value) {
        Cookie uCookie = new Cookie(key, value);
        uCookie.setPath("/");
        uCookie.setMaxAge(1000000);
        res.addCookie(uCookie);
    }


    @RequestMapping(value = "insert/superadmin")
    public ResultModel<Void> insertSuperAdmin(){
        boeUserService.insertSuperAdmin();
        return ResultModel.success(null);
    }

}
